﻿using Infrastructure.Model;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using zipkin4net;

namespace Infrastructure.Centers
{
    /// <summary>
    /// 这是一个用于实现请求追踪的中间件类。它负责在 HTTP 请求处理中生成、传递和记录追踪信息。
    /// 通过该中间件，可以在请求的不同阶段记录服务调用的 TraceId 和 SpanId，帮助后续进行分布式追踪和日志分析。
    /// </summary>
    public class TracingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly IOptions<ServiceCenterOption> _options;

        /// <summary>
        /// 构造函数，初始化中间件所需的依赖项。
        /// </summary>
        /// <param name="next">下一个中间件的委托</param>
        /// <param name="options">服务中心的配置选项</param>
        /// <param name="httpContextAccessor">HTTP上下文访问器</param>
        public TracingMiddleware(RequestDelegate next, IOptions<ServiceCenterOption> options, IHttpContextAccessor httpContextAccessor)
        {
            _next = next;
            _options = options;
        }

        /// <summary>
        /// 处理 HTTP 请求的方法。该方法会检查并生成 TraceId 和 SpanId，用于分布式追踪。
        /// </summary>
        /// <param name="context">HTTP上下文，包含请求和响应信息</param>
        public async Task InvokeAsync(HttpContext context)
        {
            // 获取当前请求是否启用追踪
            bool IsTracing = ((context.Features.Get<IEndpointFeature>()?.Endpoint)?.Metadata?.GetMetadata<NoTraceAttribute>())?.IsTracing ?? true;

            // 如果启用追踪，则输出请求头的数量
            if (IsTracing)
            {
                Console.WriteLine("Header Count :" + context.Request.Headers.Count());
            }

            // 获取 trace_id 和 pspan_id
            StringValues traceId;
            bool isFirst = !context.Request.Headers.TryGetValue("trace_id", out traceId);
            _ = !context.Request.Headers.TryGetValue("pspan_id", out var pspanid);
            long curSpanId = NumberUtils.GuidToLong(Guid.NewGuid().ToString());

            Trace trace;

            // 如果是第一次请求，创建新的 trace 和 SpanState
            if (isFirst)
            {
                long trcId = NumberUtils.GuidToLong(Guid.NewGuid().ToString()); // 生成新的 TraceId
                trace = Trace.CreateFromId(new SpanState(0L, trcId, null, curSpanId, true, isDebug: false));

                // 如果启用了追踪，设置请求和响应头中的 trace_id 和 pspan_id
                if (IsTracing)
                {
                    context.Request.Headers.Add("trace_id", NumberUtils.EncodeLongToLowerHexString(trcId)); // 添加 TraceId 到请求头
                    context.Request.Headers.Add("pspan_id", NumberUtils.EncodeLongToLowerHexString(curSpanId)); // 添加 ParentSpanId 到请求头
                    context.Response.Headers.Add("trace_id", NumberUtils.EncodeLongToLowerHexString(trcId)); // 添加 TraceId 到响应头

                    // 输出 TraceId 和 ParentId
                    Console.WriteLine("First TraceId:" + NumberUtils.EncodeLongToLowerHexString(trcId));
                    Console.WriteLine("First ParentId:" + NumberUtils.EncodeLongToLowerHexString(curSpanId));

                    // 记录追踪信息
                    trace.Record(Annotations.ClientSend()); // 客户端发送请求
                    trace.Record(Annotations.ServiceName(_options.Value.ServiceName)); // 服务名称
                    trace.Record(Annotations.Rpc(context.Request.Path)); // 请求的路径
                }
            }
            else
            {
                // 如果不是第一次请求，获取传入的 TraceId 和 ParentId
                Console.WriteLine("be4 Incoming TraceId:" + traceId);
                Console.WriteLine("be4 Incoming ParentId:" + pspanid);

                // 解码传入的 TraceId
                long trcId = NumberUtils.DecodeHexString(traceId.ToString());
                context.Request.Headers["pspan_id"] = NumberUtils.EncodeLongToLowerHexString(curSpanId); // 设置当前 SpanId

                // 输出接收到的 TraceId 和 ParentId
                Console.WriteLine("af  Incoming TraceId:" + NumberUtils.EncodeLongToLowerHexString(trcId));
                Console.WriteLine("af  Incoming CurentSId:" + NumberUtils.EncodeLongToLowerHexString(curSpanId));

                // 解码 ParentSpanId
                long ParentSId = NumberUtils.DecodeHexString(pspanid);
                trace = Trace.CreateFromId(new SpanState(0L, trcId, ParentSId, curSpanId, true, isDebug: false));

                // 记录追踪信息
                trace.Record(Annotations.ServerRecv()); // 服务器接收到请求
                trace.Record(Annotations.ServiceName(_options.Value.ServiceName)); // 服务名称
                trace.Record(Annotations.Rpc(context.Request.Path)); // 请求的路径
            }

            // 调用下一个中间件
            await _next(context);

            // 对响应头的 key 进行去重
            context.Response.Headers.Select<KeyValuePair<string, StringValues>, string>((KeyValuePair<string, StringValues> x) => x.Key).Distinct();

            // 记录响应结果信息
            if (isFirst)
            {
                trace.Record(Annotations.Tag("Result", context.Response.StatusCode.ToString())); // 记录响应状态码
                trace.Record(Annotations.ClientRecv()); // 客户端接收到响应
            }
            else
            {
                trace.Record(Annotations.Tag("Result", context.Response.StatusCode.ToString())); // 记录响应状态码
                trace.Record(Annotations.ServerSend()); // 服务器发送响应
            }
        }
    }
}
